//
// Created by entaoyang@163.com on 2021/8/2.
//

import Foundation

public extension Array {
    var toList: List<Element> {
        List(self)
    }
}

public extension List {
    func popFirst() -> Element? {
        if self.isEmpty {
            return nil
        }
        return self.remove(at: 0)
    }


}

public final class List<Element>: NSObject, RandomAccessCollection,
        MutableCollection, ExpressibleByArrayLiteral, RangeReplaceableCollection,
        CustomReflectable {
    public typealias Index = Int
    public typealias Indices = Range<Int>
    public typealias Iterator = IndexingIterator<[Element]>
    public typealias SubSequence = ArraySlice<Element>
    public typealias ArrayLiteralElement = Element

    fileprivate var array: [Element] = []

    var toArray: Array<Element> {
        self.array
    }

    required override public init() {
        super.init()
        array.reserveCapacity(16)
    }

    convenience public init(array: [Element]) {
        self.init()
        self.array.append(contentsOf: array)
    }

    required public init(arrayLiteral elements: Element...) {
        array.reserveCapacity(elements.count)
        array.append(contentsOf: elements)
    }

    required public init<S>(_ s: S) where Element == S.Element, S: Sequence {
        let n = s.underestimatedCount
        if n > 0 {
            array.reserveCapacity(n)
        }
        array.append(contentsOf: s)
    }

    required public init(repeating repeatedValue: Element, count: Int) {
        array = Array(repeating: repeatedValue, count: count)
    }

    public init(from decoder: Decoder) throws where Element: Decodable {
        array = try Array(from: decoder)
    }

    public func encode(to encoder: Encoder) throws where Element: Encodable {
        try array.encode(to: encoder)
    }

    public override var description: String {
        array.description
    }

    public override var debugDescription: String {
        array.debugDescription
    }

    public func reserveCapacity(_ minimumCapacity: Int) {
        self.enterSync()
        defer {
            self.exitSync()
        }
        array.reserveCapacity(minimumCapacity)
    }

    public func append(_ newElement: Element) {
        self.enterSync()
        defer {
            self.exitSync()
        }
        array.append(newElement)
    }

    public func append<S>(contentsOf newElements: S) where Element == S.Element, S: Sequence {
        self.enterSync()
        defer {
            self.exitSync()
        }
        array.append(contentsOf: newElements)
    }

    @discardableResult
    public func remove(at index: Int) -> Element {
        self.enterSync()
        defer {
            self.exitSync()
        }
        return array.remove(at: index)
    }

    public func insert(_ newElement: Element, at i: Int) {
        self.enterSync()
        defer {
            self.exitSync()
        }
        array.insert(newElement, at: i)
    }

    public func clear() {
        self.removeAll(keepingCapacity: true)
    }

    public func removeAll(keepingCapacity keepCapacity: Bool = false) {
        self.enterSync()
        defer {
            self.exitSync()
        }
        array.removeAll(keepingCapacity: keepCapacity)
    }

    public func withContiguousMutableStorageIfAvailable<R>(_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R) rethrows -> R? {
        self.enterSync()
        defer {
            self.exitSync()
        }
        return try array.withContiguousMutableStorageIfAvailable(body)
    }

    public func withContiguousStorageIfAvailable<R>(_ body: (UnsafeBufferPointer<Element>) throws -> R) rethrows -> R? {
        try array.withContiguousStorageIfAvailable(body)
    }


    public var startIndex: Int {
        array.startIndex
    }

    public var endIndex: Int {
        array.endIndex
    }

    public func index(after i: Int) -> Int {
        array.index(after: i)
    }

    public func formIndex(after i: inout Int) {
        array.formIndex(after: &i)
    }

    public func index(before i: Int) -> Int {
        array.index(before: i)
    }

    public func formIndex(before i: inout Int) {
        array.formIndex(before: &i)
    }

    public func index(_ i: Int, offsetBy distance: Int) -> Int {
        array.index(i, offsetBy: distance)
    }

    public func index(_ i: Int, offsetBy distance: Int, limitedBy limit: Int) -> Int? {
        array.index(i, offsetBy: distance, limitedBy: limit)
    }

    public func distance(from start: Int, to end: Int) -> Int {
        array.distance(from: start, to: end)
    }


    public subscript(bounds: Range<Int>) -> ArraySlice<Element> {
        array[bounds]
    }

    public var count: Int {
        array.count
    }
    public subscript(index: Int) -> Element {
        get {
            self.enterSync()
            defer {
                self.exitSync()
            }
            return array[index]
        }
        set {
            set(index, value: newValue)
        }
    }

    public func makeIterator() -> IndexingIterator<[Element]> {
        array.makeIterator()
    }

    public var customMirror: Mirror {
        array.customMirror
    }

    public func get(_ index: Int) -> Element? {
        self.enterSync()
        defer {
            self.exitSync()
        }
        if index >= 0 && index < self.count {
            return array[index]
        }
        return nil
    }

    public func set(_ index: Int, value: Element) {
        self.enterSync()
        defer {
            self.exitSync()
        }
        if index >= 0 && index < self.count {
            array[index] = value
        } else if index == self.count {
            array.append(value)
        } else {
            fatalError("Index out range!")
        }
    }

}

//extension List: Encodable where Element: Encodable {
//
//}

public extension List where Element: Hashable {
    func hash(into hasher: inout Hasher) {
        array.hash(into: &hasher)
    }

    var hashValue: Int {
        array.hashValue
    }
}

public extension List {
    func replaceSubrange<C>(_ subrange: Range<Int>, with newElements: C) where Element == C.Element, C: Collection {
        self.enterSync()
        defer {
            self.exitSync()
        }
        self.array.replaceSubrange(subrange, with: newElements)
    }


    static func ==(lhs: List<Element>, rhs: List<Element>) -> Bool where Element: Equatable {
        lhs.array == rhs.array
    }

    static func ==(lhs: List<Element>, rhs: Array<Element>) -> Bool where Element: Equatable {
        lhs.array == rhs
    }

    static func +(lhs: List<Element>, rhs: Array<Element>) -> List<Element> {
        List(lhs.array + rhs)
    }

    static func +(lhs: List<Element>, rhs: List<Element>) -> List<Element> {
        List(lhs.array + rhs.array)
    }

    static func +=(lhs: List<Element>, rhs: Array<Element>) {
        lhs.enterSync()
        defer {
            lhs.exitSync()
        }
        lhs.array += rhs
    }

    static func +=(lhs: List<Element>, rhs: List<Element>) {
        lhs.enterSync()
        defer {
            lhs.exitSync()
        }
        lhs.array += rhs.array
    }

    static func +=(lhs: List<Element>, rhs: Element) {
        lhs.append(rhs)
    }

    var capacity: Int {
        array.capacity
    }

    @discardableResult
    func remove(value: Element) -> Element? where Element: Equatable {
        self.enterSync()
        defer {
            self.exitSync()
        }
        for i in 0..<self.count {
            if self[i] == value {
                return self.remove(at: i)
            }
        }
        return nil
    }


    func removeAll(value: Element) where Element: Equatable {
        self.enterSync()
        defer {
            self.exitSync()
        }
        var ns: [Int] = []
        for i in 0..<self.count {
            if self[i] == value {
                ns.append(i)
            }
        }
        ns.reverse()
        for n in ns {
            let _ = self.remove(at: n)
        }
    }

    func removeValue(_ block: (Element) -> Bool) {
        self.enterSync()
        defer {
            self.exitSync()
        }
        var ns: [Int] = []
        for i in 0..<self.count {
            if block(self[i]) {
                ns.append(i)
            }
        }
        ns.reverse()
        for n in ns {
            let _ = self.remove(at: n)
        }
    }
}
